/*
 * Copyright 2008 Federal Emergency Management Agency
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0 
 *    
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 *  
 */
package gov.fema.hazus.bdc.client.ui;

import java.util.HashMap;

import gov.fema.hazus.bdc.client.Constants;
import gov.fema.hazus.bdc.client.MainUI;
import gov.fema.hazus.bdc.client.UIController;
import gov.fema.hazus.bdc.client.common.entities.AbstractClickListener;
import gov.fema.hazus.bdc.client.common.handlers.LoggingAsyncHandler;
import gov.fema.hazus.bdc.client.common.ui.InCASTTabPanel;
import gov.fema.hazus.bdc.client.common.ui.Message;
import gov.fema.hazus.bdc.client.controller.Controller;
import gov.fema.hazus.bdc.client.general.GeneralWidget;
import gov.fema.hazus.bdc.client.hazards.earthquake.EarthquakeWidget;
import gov.fema.hazus.bdc.client.hazards.flood.FloodWidget;
import gov.fema.hazus.bdc.client.hazards.wind.WindWidget;
import gov.fema.hazus.bdc.client.login.Login;
import gov.fema.hazus.bdc.client.search.Search;
import gov.fema.hazus.bdc.client.services.BuildingServiceProxy;
import gov.fema.hazus.bdc.shared.entity.Building;
import gov.fema.hazus.bdc.shared.entity.User;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

public class BuildingEditor extends Composite {
    
    /**
     * Set up instance for Login
     */
    private static BuildingEditor singleton;
    public static BuildingEditor get()
    {
        if( singleton == null )
        {
            singleton = new BuildingEditor();
        }
        return singleton;
    }
    
    
    class Updater extends LoggingAsyncHandler
    {

        public void handleFailure(Throwable caught) {
            GWT.log("Updater: Send message to UI:" + caught, null);
            Message.get().show(caught.getMessage());
        }

        public void handleSuccess(Object result) {
            
            building = (Building) result;
            
            GWT.log("Updating UI widgets with updated building:" + building, null);
            
            updateWidgets(building);
            
            Message.get().show(constants.buildingSaved());
        }

    }
    
    class CancelHandler extends LoggingAsyncHandler
    {

        public void handleFailure(Throwable caught) {
            GWT.log("Updater: Send message to UI:" + caught, null);
            Message.get().show(caught.getMessage());
        }

        public void handleSuccess(Object result) {
            
            // switch to the building list, updated
            LeftNavigation.get().showBuildingList();
            
        }

    }    

    public Updater updater = new Updater();
    public CancelHandler cancelHandler = new CancelHandler();
    
    class CancelButtonClickListener extends AbstractClickListener
    {

        @Override
        public void handleClick(Widget sender) {
            
            Message.get().hide();
            if( building.getId() != null )
            {
                // release the building via the API
            	BuildingServiceProxy.get().releaseBuilding(building, cancelHandler);
            }
            else
            {
                // building was never saved, so just go back to list view
                Controller.get().mainShowBuildingList();
            }
            

        }
        
    }        
    class SaveButtonClickListener extends AbstractClickListener
    {

        @Override
        public void handleClick(Widget sender) {
            // clear messages
            Message.get().hide();
            
            generalWidget.updateName();
            
            GWT.log("SaveButtonClickListener="+sender, null);
            GWT.log("building="+building, null);
            
            /*
             * When saving a building, we want to record the 
             * user who edited it. To do this, we insert the
             * logged in User into the Building. It might
             * be already set from the edit mode, but we
             * override it here for safety.
             */
            User user = Login.get().getLoginUser();
            building.setUserName(user.getUsername());
            
            /*
             * Check for invalid fields,
             * don't save if there is any,
             * warn the user
             */
            HashMap invalidFields = Message.get().getInvalidFields();
            if( invalidFields.isEmpty())
            {
            	BuildingServiceProxy.get().saveBuilding(building, updater);
            } else {
            	// TODO: il8n this text
            	Message.get().setText("Some fields have values that cannot be saved. Please check each field.");
            	Message.get().show();
            }

        }
        
    }    
    
    class BuildingDetailUpdater extends LoggingAsyncHandler
    {

        public void handleFailure(Throwable caught) {
            GWT.log("UserDetailUpdater: Send message to UI:" + caught, null);
            Message.get().show(caught.getMessage());
        }

        public void handleSuccess(Object result) {
            
            // set the color back if we were on a search previously
            
            UIController.get().setSearchBorder(false);
            LeftNavigation.get().highlightLeftNav(constants.leftnavListBuildings());
            Search.get().clearQuery();
            
            building = (Building) result;
            
            GWT.log("Updating building detail:" + building, null);
            
            // run the building list through the formatter
            updateWidgets(building);
            
            // clear the message
            Message.get().hide();
            
            // make this visible
            ////showBuildingDetail();
            BuildingList.get().setVisible(false);
            
            // hide the building list
            //InCAST.get().hideBuildingList();
            //InCAST.get().highlightLeftNav(constants.leftnavListBuildings());
        }

    }    
    
    public void setBuilding(Building building) {
        this.building = building;
    }

    // entities
    public Building building;
    
    // widgets
    private GeneralWidget generalWidget;
    private EarthquakeWidget earthquakeWidget;
    private FloodWidget floodWidget;
    private WindWidget windWidget;
    //private NotesWidget notesWidget;
    
    // handlers
    BuildingEditorHandler buildingEditorHandler = new BuildingEditorHandler();


    private VerticalPanel panel = new VerticalPanel();
    private Constants constants;
    private DockPanel docker;
    private InCASTTabPanel tabPanel;
    private VerticalPanel buttonsNameDisplay;
    private Label prominentBuildingName;
    private HorizontalPanel northButtonBar;
    private HorizontalPanel southButtonBar;
    private BuildingDetailUpdater buildingDetailUpdater;
    private HorizontalPanel panelTitle;
    private Button saveButton = new Button();
    private Button cancelButton = new Button();
    private Label viewMap;
	private HTML userLocked;
    
    
    public BuildingEditor()
    {
        singleton = this;

        this.building = new Building();
        
        constants = (Constants)GWT.create(Constants.class);
        docker  = new DockPanel();
        tabPanel = new InCASTTabPanel();
        buttonsNameDisplay = new VerticalPanel();
        
        // display the building name prominently
        String buildingName = "";
        if (building != null )
        {
            buildingName = building.getName();
        }
        prominentBuildingName = new Label(buildingName);
        prominentBuildingName.setStyleName("building-name");
        
        // widgets
        generalWidget = new GeneralWidget(building);
        earthquakeWidget = new EarthquakeWidget(building);
        windWidget = new WindWidget(building);
        floodWidget = new FloodWidget(building);
        
        // notes are experimental, so hold off for now
        // notesWidget = new NotesWidget(building); 
        
        tabPanel.setWidth("100%");
        tabPanel.setHeight("100%");
        tabPanel.add(generalWidget, constants.generalTabTitle());
        tabPanel.add(earthquakeWidget, constants.earthquakeTabTitle());
        tabPanel.add(windWidget, constants.windTabTitle());
        tabPanel.add(floodWidget, constants.floodTabTile());
        
        // notes are experimental, so hold off for now
        // tabPanel.add(notesWidget, constants.notesTitle() );
        
        tabPanel.selectTab(0);
        tabPanel.setStyleName("building-detail-tabpanel");
        
        docker.add(tabPanel, DockPanel.CENTER);
        docker.setCellVerticalAlignment(tabPanel, HasVerticalAlignment.ALIGN_TOP);
        docker.setCellHeight(tabPanel, "100%");
        docker.setCellWidth(tabPanel, "100%");
        
        // north button bar
        northButtonBar = new HorizontalPanel();
        northButtonBar.setStyleName("north-button-bar");
        northButtonBar.add(prominentBuildingName);
    
        // south button bar
        southButtonBar = new HorizontalPanel();
        
        // save button
        saveButton = new Button();
        saveButton.setStyleName("incast-command-button");
        saveButton.setText(constants.saveButton());
        saveButton.addClickListener(new SaveButtonClickListener());

        // cancel button
        cancelButton = new Button();
        cancelButton.setStyleName("incast-command-button");
        cancelButton.setText(constants.cancelButton());
        cancelButton.addClickListener(new CancelButtonClickListener());
        
        // view in map command
        viewMap = new Label(constants.viewInMap());
        viewMap.setStyleName("clickable-text");
        viewMap.addClickListener(new ClickListener(){
            public void onClick(Widget sender) {
                //TODO hook UIController.get().showBuildingInMap(building);
            }
        });
        
        /*
         * Put save, cancel in a fixed width 
         * flex table, then in the horizontal panel
         * with a left alignment
         */
        FlexTable bottomCommands = new FlexTable();
        bottomCommands.setWidth("");
        
        bottomCommands.setWidget(0, 0, saveButton);
        bottomCommands.getColumnFormatter().setWidth(0, "10");
        
        bottomCommands.setWidget(0, 1, cancelButton);
        bottomCommands.getColumnFormatter().setWidth(1, "10");
        
        /*
         * Lock Indicator next to Cancel button
         * If the building is locked, and the current user cannot edit
         * then indicate the other user who has it locked
         */
        userLocked = new HTML();
        updateUserLockedStatus();
        bottomCommands.setWidget(0, 2, userLocked);
        bottomCommands.getColumnFormatter().setWidth(2, "100%");
        
        southButtonBar.add(bottomCommands);
        southButtonBar.setStyleName("south-button-bar");
        
        buttonsNameDisplay.add(prominentBuildingName);
        buttonsNameDisplay.setStyleName("north-button-bar");
        buttonsNameDisplay.add(northButtonBar);
        
        docker.add(buttonsNameDisplay, DockPanel.NORTH);
        docker.add(southButtonBar, DockPanel.SOUTH);
        
        
        docker.setStyleName("docker-building-detail");
        docker.setHeight("100%");
        
        // updaters
        buildingDetailUpdater = new BuildingDetailUpdater();
        
        initWidget(docker);
        
    }
    
    
    public void getBuilding(Building building)
    {
        // pull user from session, set in building - for AuthAPI
    	User currentUser = Login.get().getLoginUser();
    	building.setUserName(currentUser.getUsername());
        
        BuildingServiceProxy.get().getBuilding(building, buildingEditorHandler);
    }
    
    private class BuildingEditorHandler extends LoggingAsyncHandler
    {

        @Override
        public void handleFailure(Throwable caught) {
            GWT.log("failure=" + caught.getMessage(), null);
            Message.get().show(caught.getMessage());
        }

        @Override
        public void handleSuccess(Object result) {
            
            
            GWT.log("fetched building for editing="+result, null);
            
            Message.get().hide();
            
            MainUI.get().setSearchBorder(false);
            LeftNavigation.get().highlightLeftNav(constants.leftnavListBuildings());
            Search.get().clearQuery();
            
            Building building = (Building) result;
            
            /*
             * The fetch could have been successful, but we
             * still might have a in Building.READ_ONLY mode
             * because it is being edited by another user.
             */
            if( building.getMode().equalsIgnoreCase(Building.READ_ONLY))
            {
                Message.get().show("This building is read only");
            }
            
            setBuilding(building);
            updateWidgets(building);
            Controller.get().mainShowBuildingEditor();
        }
        
    }

    
    public void updateBuilding(Building building) {
        updateWidgets(building);
    }
    
    public void updateWidgets(Building building)
    {
        
        this.building = building;
        
        if( this.building.getMode().equalsIgnoreCase(Building.READ_ONLY))
        {
            saveButton.setEnabled(false); 
            cancelButton.setEnabled(false);
        }
        else
        {
            saveButton.setEnabled(true);
            cancelButton.setEnabled(true);
        }
        
        /*
         * If we are on an AddBuilding action,
         * hide the View in Map link 
         */
        if(building.getId() == null || building.getLocation() == null
                || (building.getLocation().getLatitude() == 0 
                        && building.getLocation().getLatitude() == 0))
        {
            viewMap.setVisible(false);
        }
        else
        {
            viewMap.setVisible(true);
        }
        
        //String nameVersion = building.getName() + " V("+building.getVersion().toString()+")";
        //prominentBuildingName.setText(building.getName());
        prominentBuildingName.setText(building.getName());
        generalWidget.updateBuilding(building);
        
        earthquakeWidget.updateBuilding(building);
        floodWidget.updateBuilding(building);
        windWidget.updateBuilding(building);
        
        // notes are experimental, so hold off for now
        // notesWidget.updateBuilding(building);
        
        // set this tab panel
        tabPanel.selectTab(0);
        
        // set the general widget tab panel
        generalWidget.resetTabs();
        
        /****
        if( message != null )
        {
            message.setText("");
            message.addStyleName("clear-message");
        }
        ****/
        
        // update the building locked message
        updateUserLockedStatus();
        
        // show this
        ////showBuildingDetail();
        
    }    
    
    public void updateUserLockedStatus()
    {
    	User loggedInUser = Login.get().getLoginUser();
    	GWT.log("logged in user=" + loggedInUser, null);
    	
    	if( loggedInUser != null && loggedInUser.getUsername() != null )
    	{

	        String userWithBuildingLock = this.building.getUserName();
	        GWT.log("userWithBuildingLock=" + userWithBuildingLock, null);
	    	
	        if( !loggedInUser.getUsername().equalsIgnoreCase(userWithBuildingLock ))
	        {
	        	GWT.log("logged in user name != user with building lock", null);
	        	userLocked.setHTML("<b>" + userWithBuildingLock + "</b> " + constants.editingUser());
	        }
	        else
	        {
	        	GWT.log("logged in user name == user with building lock", null);
	        	userLocked.setHTML(constants.unlockMessage());
	        }
    	}
    }
    
    public static void actionWrapperEditBuilding(Long id)
    {
    	GWT.log("edit building id=" + id, null);
    	Building b = new Building();
    	//TODO: handle null string for building id
    	//b.setId(id);
    	BuildingEditor.get().getBuilding(b);
    } 
    
    public static void actionWrapperAddBuilding()
    {
    	//TODO: hook up add building
    }    
    
    public static double actionWrapperGetBuildingLatitude()
    {
		GWT.log("building=" + BuildingEditor.get().building, null);
		GWT.log("location=" + BuildingEditor.get().building.getLocation(), null);
		GWT.log("latitude=" + BuildingEditor.get().building.getLocation().getLatitude(), null);
    	return BuildingEditor.get().building.getLocation().getLatitude();
    }      
    
    public static double actionWrapperGetBuildingLongitude()
    {
    	return BuildingEditor.get().building.getLocation().getLongitude();
    }       
    
    public static void actionWrapperUpdateLatLng(double latitude, double longitude)
    {
    	// statically, reach into the General > Location widget and set the lat lng in the form
    	GWT.log("Sending lat/lng to UserEditor.updateLatLng()", null);
    	BuildingEditor.get().updateLatLng(latitude, longitude);
    }
    
	public static void actionWrapperSetVisible(boolean flag)
	{
		GWT.log("Executing UserEditor.get().actionWrapperSetVisible("+flag+")", null);
		BuildingEditor.get().setVisible(flag);
	}    


	public void updateLatLng(double latitude, double longitude) {
		GWT.log("Sending lat/lng to generalWidget.updateLatLng()", null);
		generalWidget.updateLatLng(latitude, longitude);
	}
}
